package com.huxi.lang.object.support;

import static com.huxi.lang.type.VTypeUtils.isBlank;
import static java.lang.reflect.Modifier.PRIVATE;
import static java.lang.reflect.Modifier.PROTECTED;
import static java.lang.reflect.Modifier.PUBLIC;
import static java.lang.reflect.Modifier.isFinal;

import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles.Lookup;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

import com.huxi.lang.object.spi.ReflectFieldDefine;
import com.huxi.lang.validation.predicate.VPredicate;

/**
 * 反射字段构建器
 * 
 * @author jian.wu
 *
 */
public class ReflectFieldBuilder implements ReflectFieldDefine {

	private static final int ACCESS_MODIFIERS = PUBLIC | PROTECTED | PRIVATE;
	@SuppressWarnings("unused")
	private static final int ACCESS_COST_MAX = 64;
	private static final int ACCESS_PUBLIC = 16;
	private static final int ACCESS_PROTECTED = 8;
	private static final int ACCESS_PRIVATE = 4;
	private static final int ACCESS_COST_MIN = 0;

	private static int calculateMergedCost(int mod) {
		int cost = mod & ACCESS_MODIFIERS;
		return cost == PUBLIC ? ACCESS_PUBLIC : cost == PROTECTED ? ACCESS_PROTECTED : ACCESS_PRIVATE;
	}

	private final String name; // 字段名称
	private Boolean serialize; // 是否需要序列化
	private Boolean deserialize; // 是否需要反序列化
	private Field field;
	private Method getter;
	private Method setter;
	private List<Method> setters;

	private int fcost = ACCESS_COST_MIN;
	private int gcost = ACCESS_COST_MIN;
	private int scost = ACCESS_COST_MIN;

	private int mergingType = 0;
	private int mergingCost = 0;
	private Object mergingFrom = null;

	private String format; // 格式模板
	private String displayName; // 字段通俗名称
	private String describe; // 字段描述
	private VPredicate predicate; // 数据校验器
	private String message; // 校验失败提示信息

	ReflectFieldBuilder(String name) {
		this.name = name;
	}

	private MethodHandle genGetter(Lookup lookup, int cost, Method method) throws IllegalAccessException {
		if (cost >= ACCESS_PUBLIC) {
			return lookup.unreflect(method);
		} else if (Boolean.TRUE == serialize) {
			method.setAccessible(true);
			return lookup.unreflect(method);
		}
		return null;
	}

	private MethodHandle genGetter(Lookup lookup, int cost, Field field) throws IllegalAccessException {
		if (cost >= ACCESS_PUBLIC) {
			return lookup.unreflectGetter(field);
		} else if (Boolean.TRUE == serialize) {
			field.setAccessible(true);
			return lookup.unreflectGetter(field);
		}
		return null;
	}

	private MethodHandle genSetter(Lookup lookup, int cost, Method method) throws IllegalAccessException {
		if (cost >= ACCESS_PUBLIC) {
			return lookup.unreflect(method);
		} else if (Boolean.TRUE == deserialize) {
			method.setAccessible(true);
			return lookup.unreflect(method);
		}
		return null;
	}

	private MethodHandle genSetter(Lookup lookup, int cost, Field field) throws IllegalAccessException {
		if (isFinal(field.getModifiers())) {
			return null;
		}
		if (fcost >= ACCESS_PUBLIC) {
			return lookup.unreflectGetter(field);
		} else if (Boolean.TRUE == deserialize) {
			field.setAccessible(true);
			return lookup.unreflectGetter(field);
		}
		return null;
	}

	private ReflectField append(ReflectField field) {
		if (field != null) {
			field.withFormat(format).withDisplayName(displayName).withDescribe(describe).withPredicate(predicate)
					.withMessage(message);
		}
		return field;
	}

	private ReflectField genFieldReflect(Lookup lookup, MethodHandle getter) throws IllegalAccessException {
		MethodHandle msetter = genSetter(lookup, fcost, field);
		if (getter == null && msetter == null) {
			return null;
		}
		return append(new ReflectField.ExactField(name, getter, msetter));
	}

	protected ReflectField genMethodReflect(Lookup lookup, MethodHandle getter, Field field)
			throws IllegalAccessException {
		MethodHandle setter = genSetter(lookup, scost, this.setter);
		if (setter == null) {
			return getter != null ? append(new ReflectField.ExactField(name, getter, null)) : null;
		}

		boolean fhited = field == null || field.getType() == setter.type().parameterType(1);

		Class<?> expect = getter != null ? getter.type().returnType() : null;
		MethodHandle mhited = expect == null || expect == setter.type().parameterType(1) ? setter : null;

		ArrayList<MethodHandle> others = null;
		if (!(setters == null || setters.size() == 0)) {
			others = new ArrayList<>(setters.size() + 1);
			for (Method mm : setters) {
				MethodHandle mh = genSetter(lookup, scost, mm);
				if (mhited == null && expect == mh.type().parameterType(1)) {
					mhited = mh;
				} else {
					others.add(mh);
				}
				if (!fhited && field.getType() == mh.type().parameterType(1)) {
					fhited = true;
				}
			}
		}
		if (!fhited) {
			MethodHandle mh = genSetter(lookup, scost, field);
			if (mh != null) {
				if (others == null) {
					others = new ArrayList<>(1);
				}
				others.add(mh);
			}
		}
		if (mhited != setter) {
			others.add(setter);
		}
		return append(new ReflectField.RouteField(name, getter, mhited,
				others == null ? Collections.emptyList() : Collections.unmodifiableList(others)));
	}

	ReflectField build(Lookup lookup) throws IllegalAccessException {
		MethodHandle mgetter = null;
		if (Boolean.FALSE != serialize) {
			if (gcost > fcost) {
				mgetter = genGetter(lookup, gcost, getter);
			} else if (gcost == fcost) {
				if (gcost != ACCESS_COST_MIN) {
					// 同级别的访问等级时，函数优先于字段。
					if (!getter.getReturnType().isPrimitive() && field.getType().isPrimitive()) {
						mgetter = genGetter(lookup, fcost, field);
					} else {
						mgetter = genGetter(lookup, gcost, getter);
					}
				}
				return null;
			} else {
				mgetter = genGetter(lookup, fcost, field);
			}
		}
		if (Boolean.FALSE == deserialize) {
			return mgetter != null ? append(new ReflectField.ExactField(name, mgetter, null)) : null;
		}
		if (scost > fcost) {
			return genMethodReflect(lookup, mgetter, null);
		} else if (scost == fcost) {
			if (scost != ACCESS_COST_MIN) {
				return genMethodReflect(lookup, mgetter, field);
			}
			return null;
		} else {
			return genFieldReflect(lookup, mgetter);
		}
	}

	ReflectFieldBuilder merged() {
		if (1 == mergingType) {
			if (mergingCost > fcost) {
				fcost = mergingCost;
				field = (Field) mergingFrom;
			}
		} else if (2 == mergingType) {
			if (mergingCost > gcost) {
				gcost = mergingCost;
				getter = (Method) mergingFrom;
			} else if (mergingCost == gcost) {
				Method mm = (Method) mergingFrom;
				if (getter.getName().length() > mm.getName().length()) {
					// isXxx() 优先高于 getXxx() 函数
					getter = mm;
				}
			}
		} else if (4 == mergingType) {
			if (mergingCost == scost) {
				// 同级别的访问等级
				if (null == setters) {
					setters = new ArrayList<>();
				}
				setters.add((Method) mergingFrom);
			} else {
				int cost = mergingCost & scost;
				if (cost >= ACCESS_PUBLIC) {
					// 同级别的访问等级
					if (null == setters) {
						setters = new ArrayList<>();
					}
					if (mergingCost > scost) {
						// 注释优先级高于无注释
						setters.add(setter);
						scost = mergingCost;
						setter = (Method) mergingFrom;
					} else {
						setters.add((Method) mergingFrom);
					}
				} else {
					// 非同级别的访问等级
					if (mergingCost > scost) {
						// 访问等级更高
						if (null != setters) {
							setters.clear();
						}
						scost = mergingCost;
						setter = (Method) mergingFrom;
					}
				}
			}
		}
		mergingType = 0;
		mergingCost = ACCESS_COST_MIN;
		mergingFrom = null;
		return this;
	}

	boolean mergeField(Field field) {
		if (this.field != null) {
			// 字段子类优先，首次有效。
			return false;
		}
		if (Modifier.isTransient(field.getModifiers())) {
			// 非序列化字段
			serialize = Boolean.FALSE;
			deserialize = Boolean.FALSE;
			return false;
		}
		mergingType = 1;
		mergingCost = calculateMergedCost(field.getModifiers());
		mergingFrom = field;
		return true;
	}

	boolean mergeGetMethod(Method method) {
		if (Boolean.FALSE == serialize) {
			// 非序列化字段
			return false;
		}
		if (this.getter != null) {
			if (this.getter.getDeclaringClass() != method.getDeclaringClass()) {
				// 函数重载子类优先，首次有效。
				return false;
			}
		}
		mergingType = 2;
		mergingCost = calculateMergedCost(method.getModifiers());
		mergingFrom = method;
		return true;
	}

	boolean mergeSetMethod(Method method) {
		if (Boolean.FALSE == deserialize) {
			// 非反序列化字段
			return false;
		}
		if (this.setter != null) {
			if (this.setter.getDeclaringClass() != method.getDeclaringClass()) {
				// 函数重载子类优先，首次有效。目标是友好的PORO对象，使用快速的粗暴算法，只保留子类中的重载方法。
				return false;
			}
		}
		mergingType = 4;
		mergingCost = calculateMergedCost(method.getModifiers());
		mergingFrom = method;
		return true;
	}

	public ReflectFieldBuilder withAccessable(boolean serialize, boolean deserialize) {
		return withSerialize(serialize).withDeserialize(deserialize);
	}

	public ReflectFieldBuilder withSerialize(boolean serialize) {
		if (Boolean.FALSE != this.serialize) {
			if (this.serialize && mergingType != 4) {
				// 有注释时优先级提升一档
				mergingCost += 1;
			}
			this.serialize = serialize ? Boolean.TRUE : Boolean.FALSE;
		}
		return this;
	}

	public ReflectFieldBuilder withDeserialize(boolean deserialize) {
		if (Boolean.FALSE != this.deserialize) {
			if (deserialize && mergingType != 2) {
				mergingCost += 2;
			}
			this.deserialize = deserialize ? Boolean.TRUE : Boolean.FALSE;
		}
		return this;
	}

	public ReflectFieldBuilder withFormat(String format) {
		if (!isBlank(format) && isBlank(this.format)) {
			this.format = format;
		}
		return this;
	}

	public ReflectFieldBuilder withDisplayName(String displayName) {
		if (!isBlank(displayName) && isBlank(this.displayName)) {
			this.displayName = displayName;
		}
		return this;
	}

	public ReflectFieldBuilder withDescribe(String describe) {
		if (!isBlank(describe) && isBlank(this.describe)) {
			this.describe = describe;
		}
		return this;
	}

	public ReflectFieldBuilder withPredicate(VPredicate predicate) {
		if (predicate != null) {
			if (this.predicate != null) {
				this.predicate = predicate.and(this.predicate);
			}
		}
		return this;
	}

	public ReflectFieldBuilder withMessage(String message) {
		if (!isBlank(message) && isBlank(this.message)) {
			this.message = message;
		}
		return this;
	}
}
